/* map_area.c */

#include <stdlib.h>

#include "SDL.h"
#include "SDL_gfxPrimitives.h"

#include "map_area.h"
#include "graphics.h"
#include "selection.h"
#include "path.h"
#include "main_window.h"
#include "pony.h"
#include "ponyedit.h"
#include "maps.h"

static EFFECTS effects;
static int dragging_point = -1;
static int dragging_statue = -1;
static int drag_statue_x = -1;
static int drag_statue_y = -1;
static int motion_redraw_delay = -1;

static int running_next_pony_delay;
static int running;

static void update_segment_shadow(PATH *path, int x, int y)
{
  int sel_point;

  sel_get_point(&sel_point);
  if (sel_point >= 0) {
    PATH_POINT *sel = path->points + sel_point;
    int dest_point = path_get_point_at(path, x, y);
    
    if (dest_point >= 0) {
      effects.shadow_segment = 1;
      effects.shadow_segment_x1 = sel->x;
      effects.shadow_segment_y1 = sel->y;
      effects.shadow_segment_x2 = path->points[dest_point].x;
      effects.shadow_segment_y2 = path->points[dest_point].y;
    } else
      effects.shadow_segment = 0;
  }
}

static void update_point_shadow(PATH *path, int x, int y)
{
  int sel_point;

  sel_get_point(&sel_point);
  if (sel_point < 0) {
    effects.shadow_point = 1;
    effects.shadow_point_x1 = x;
    effects.shadow_point_y1 = y;
    effects.shadow_point_x2 = x;
    effects.shadow_point_y2 = y;
  } else {
    PATH_POINT *sel = path->points + sel_point;
    
    effects.shadow_point = 1;
    effects.shadow_point_x1 = sel->x;
    effects.shadow_point_y1 = sel->y;
    effects.shadow_point_x2 = x;
    effects.shadow_point_y2 = y;
  }
}

/* --- KEYUP event --- */
static int handle_key_up(SDL_Event *ev, PATH *path, int mouse_x, int mouse_y)
{
  switch (ev->key.keysym.sym) {
  case SDLK_LSHIFT:
  case SDLK_RSHIFT:
    set_footer_text("");
    effects.shadow_point = 0;
    return 1;
    
  case SDLK_LCTRL:
  case SDLK_RCTRL:
    set_footer_text("");
    effects.shadow_segment = 0;
    return 1;

  case SDLK_LALT:
  case SDLK_RALT:
    set_footer_text("");
    return 1;
    
  default:
    /* printf("KEY RELEASED: '%d'\n", ev->key.keysym.sym); */
    break;
  }

  return 0;
}

/* --- KEYDOWN event --- */
static int handle_key_down(SDL_Event *ev, PATH *path, int mouse_x, int mouse_y)
{
  switch (ev->key.keysym.sym) {
  case SDLK_LSHIFT:
  case SDLK_RSHIFT:
    set_footer_text("Click to add point");
    update_point_shadow(path, mouse_x, mouse_y);
    return 1;
    
  case SDLK_LCTRL:
  case SDLK_RCTRL:
    set_footer_text("Select point and click to add segment");
    update_segment_shadow(path, mouse_x, mouse_y);
    return 1;

  case SDLK_LALT:
  case SDLK_RALT:
    set_footer_text("Click point to toggle spawn point");
    return 1;

  case SDLK_RETURN:
    {
      int sel_point, sel_segment;

      sel_get_segment(&sel_point, &sel_segment);
      if (sel_point >= 0 && sel_segment >= 0) {
        int x1, y1, x2, y2, end_point;

        end_point = path->points[sel_point].segs[sel_segment].dest_point;

        if (path_get_segment_endpoints(path, sel_point, sel_segment, &x1, &y1, &x2, &y2) == 0) {
          int new_point;

          path_remove_segment(path, sel_point, sel_segment);
          sel_clear();
          new_point = path_create_point(path, (x1 + x2) / 2, (y1 + y2) / 2);
          path_create_segment(path, sel_point, new_point, 1);
          path_create_segment(path, new_point, end_point, 1);
          sel_set_point(new_point);
        }
      }
    }
    return 1;
    
  case SDLK_DELETE:
    {
      int sel_point;
      sel_get_point(&sel_point);
      if (sel_point >= 0) {
	path_remove_point(path, sel_point);
	sel_clear();
      } else {
        int sel_segment;

        sel_get_segment(&sel_point, &sel_segment);
        if (sel_point >= 0 && sel_segment >= 0) {
          path_remove_segment(path, sel_point, sel_segment);
          sel_clear();
        }
      }
    }
    return 1;
    
  case SDLK_ESCAPE:
    SDL_Quit();
    break;

  case SDLK_TAB:
    pony_reset_all();
    running_next_pony_delay = 0;
    running = ! running;
    return 1;
    
  default:
    /* printf("KEY PRESSED: '%d'\n", ev->key.keysym.sym); */
    break;
  }

  return 0;
}

/* --- MOUSEMOTION event --- */
static int handle_mouse_motion(int x, int y, PATH *path)
{
  SDLMod mod = SDL_GetModState();

  if (dragging_point >= 0) {
    path_move_point(path, dragging_point, x, y);
    return 1;
  }

  if (dragging_statue >= 0) {
    path->statue.x = x + drag_statue_x;
    path->statue.y = y + drag_statue_y;
    return 1;
  }

  /* SHIFT */
  if ((mod & (KMOD_LSHIFT|KMOD_RSHIFT)) != 0) {
    update_point_shadow(path, x, y);
    return 1;
  }

  /* CTRL */
  if ((mod & (KMOD_LCTRL|KMOD_RCTRL)) != 0) {
    update_segment_shadow(path, x, y);
    return 1;
  }

  return 0;
}

/* --- MOUSEBUTTONDOWN event --- */
static int handle_mouse_button(int button, int x, int y, PATH *path)
{
  SDLMod mod = SDL_GetModState();

  switch (button) {
  case SDL_BUTTON_LEFT:
    /* SHIFT+click: create point */
    if ((mod & (KMOD_LSHIFT|KMOD_RSHIFT)) != 0) {
      int cur_sel_point, new_point;
      
      sel_get_point(&cur_sel_point);
      new_point = path_create_point(path, x, y);
      if (cur_sel_point >= 0)
	path_create_segment(path, cur_sel_point, new_point, 1);
      sel_set_point(new_point);
      dragging_point = new_point;
      effects.shadow_point = 0;
      return 1;
    }

    /* ALT+click: create segment */
    if ((mod & (KMOD_LALT|KMOD_RALT)) != 0) {
      int point;

      point = path_get_point_at(path, x, y);
      if (point >= 0) {
        if (path_is_spawn_point(path, point))
          path_remove_spawn_point(path, point);
        else
          path_set_spawn_point(path, point);
      }
      return 1;
    }


    /* CTRL+click: create segment */
    if ((mod & (KMOD_LCTRL|KMOD_RCTRL)) != 0) {
      int src_point, dest_point;

      sel_get_point(&src_point);
      if (src_point >= 0) {
	dest_point = path_get_point_at(path, x, y);
	if (dest_point >= 0 && dest_point != src_point) {
	  int new_seg = path_create_segment(path, src_point, dest_point, 1);
	  sel_set_segment(src_point, new_seg);
	  effects.shadow_segment = 0;

	  update_segment_info(src_point, new_seg);
	}
      }
      return 1;
    }

    /* no keys pressed, select and start dragging */
    if (mod == 0) {
      int new_point, new_segment;

      sel_clear();

      /* check statue */
      {
	int dx = path->statue.x - x;
	int dy = path->statue.y - y;
	if (dx*dx + dy*dy < path->statue.radius*path->statue.radius) {
	  dragging_statue = 1;
	  drag_statue_x = dx;
	  drag_statue_y = dy;
	}
      }

      /* check segments */
      path_get_segment_at(path, x, y, &new_point, &new_segment);
      if (new_point >= 0 && new_segment >= 0) {
	sel_set_segment(new_point, new_segment);
	update_segment_info(new_point, new_segment);
      }

      /* check points */
      new_point = path_get_point_at(path, x, y);
      if (new_point >= 0) {
	sel_set_point(new_point);
	dragging_point = new_point;
      }

      return 1;
    }    

    /* unhandled modifiers */
    break;
  }

  return 0;
}

/*
 * p: pointer to current PATH *
 * d1: border size
 */
int dlg_w_arena_proc(DLG_WIDGET *widget, int msg, unsigned int p1, unsigned int p2, void *p)
{
  PATH *path = *(PATH **) widget->p;

  switch (msg) {
  case DLGM_INIT:
    {
      PATH **ppath = (PATH **) widget->p;

      if (read_maps() != 0) {
        map_name_items[0] = "map01";
        map_name_items[1] = NULL;
        path_clear(&map_paths[0]);
      }
      *ppath = &map_paths[0];
      effects.shadow_point = 0;
      update_map_info();
    }
    break;

  case DLGM_REDRAW:
    {
      unsigned int color = (widget->dlg->focus == widget) ? 0xff0000ff : 0xff;
      int x = widget->x + widget->d1;
      int y = widget->y + widget->d1;
      //int w = widget->w - 2*widget->d1;
      //int h = widget->w - 2*widget->d1;

      SDL_FillRect(widget->dlg->screen, NULL, 0);

      //draw_rectangle(widget->dlg->screen, x-1, y-1, w+2, h+2, color);
      if (! running) {
	draw_arena(x, y, path, &effects);
      } else {
	int i;
	SDL_Rect rect = { x, y, 0, 0 };
	
	SDL_BlitSurface(map_image, 0, screen, &rect);
	
	for (i = 0; i < NUM_PONIES; i++) {
	  if (pony[i].active) {
	    int x, y, angle;
	    
	    pony_get_pos(pony + i, path, &x, &y, &angle);
	    rect.x = x - pony_image->w/2;
	    rect.y = y - pony_image->h/2;
	    SDL_BlitSurface(pony_image, 0, screen, &rect);
	  }
	}
      }
      draw_rectangle(widget->dlg->screen, widget->x, widget->y, widget->w, widget->h, color);

    }
    return 1;

  case DLGM_IDLE:
    if (! running) {
      SDL_Delay(1000/60);
      if (motion_redraw_delay >= 0 && --motion_redraw_delay <= 0)
	dlg_send_msg(widget, DLGM_REDRAW, 0, 0, NULL);
    } else {
      int i;

      if (running_next_pony_delay-- <= 0) {
	pony_spawn();
	running_next_pony_delay = 200;
      }

      for (i = 0; i < NUM_PONIES; i++)
	if (pony[i].active)
	  pony_advance(pony + i, path, 1);

      dlg_send_msg(widget, DLGM_REDRAW, 0, 0, NULL);
    }
    return 1;

  case DLGM_LOSE_FOCUS:
    set_footer_text("");
    effects.shadow_segment = 0;
    effects.shadow_point = 0;
    running = 0;
    dlg_send_msg(widget, DLGM_REDRAW, 0, 0, NULL);
    break;

  case DLGM_GAIN_FOCUS:
    if (! running)
      dlg_send_msg(widget, DLGM_REDRAW, 0, 0, NULL);
    break;
    
  case DLGM_SDL_EVENT:
    {
      SDL_Event *ev = (SDL_Event *) p;

      switch (ev->type) {
      case SDL_MOUSEBUTTONDOWN:
	if (! running) {
	  int x = ev->button.x - (widget->x + widget->d1);
	  int y = ev->button.y - (widget->y + widget->d1);
	  if (handle_mouse_button(ev->button.button, x, y, path))
	    dlg_send_msg(widget, DLGM_REDRAW, 0, 0, NULL);
	}
	break;
	
      case SDL_MOUSEBUTTONUP:
	dragging_point = -1;
	dragging_statue = -1;
	break;
	
      case SDL_MOUSEMOTION:
	if (! running) {
	  int x = (int) ev->motion.x - (widget->x + widget->d1);
	  int y = (int) ev->motion.y - (widget->y + widget->d1);

	  if (x <= - (int) widget->d1) x = 1 - (int) widget->d1;
	  if (y <= - (int) widget->d1) y = 1 - (int) widget->d1;
	  if (x >= widget->w - (int) widget->d1 - 1) x = widget->w - (int) widget->d1 - 2;
	  if (y >= widget->h - (int) widget->d1 - 1) y = widget->h - (int) widget->d1 - 2;

	  if (handle_mouse_motion(x, y, path))
	    if (motion_redraw_delay < 0)
	      motion_redraw_delay = 10;
	}
	break;
	
      case SDL_KEYUP:
	{
	  int mouse_x, mouse_y;
	  SDL_GetMouseState(&mouse_x, &mouse_y);
	  mouse_x -= widget->x + (int) widget->d1;
	  mouse_y -= widget->y + (int) widget->d1;

	  if (handle_key_up(ev, path, mouse_x, mouse_y))
	    dlg_send_msg(widget, DLGM_REDRAW, 0, 0, NULL);
	}
	break;
	
      case SDL_KEYDOWN:
	{
	  int mouse_x, mouse_y;
	  SDL_GetMouseState(&mouse_x, &mouse_y);
	  mouse_x -= widget->x + widget->d1;
	  mouse_y -= widget->y + widget->d1;

	  if (handle_key_down(ev, path, mouse_x, mouse_y))
	    dlg_send_msg(widget, DLGM_REDRAW, 0, 0, NULL);
	}
	break;
      }
    }
    return 1;

  default:
    break;
  }

  return 0;
}
